//-------------------------------------------------------------------
//	Copyright (c) 2010-2012 Zhirnov Andrey
//	This file is part of the "UXGEN" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#include "Custom.h"
#include "../../Utils/CRenderQueue.h"
#include "../../../../Resources/Textures/CTexture.h"
#include "../../CRender.h"

namespace Engine
{
namespace RenderUtils
{

//-------------------- CRTBindTexturesPass ------------------------//
/*
=================================================
	
=================================================
*/
	CRTBindTexturesPass::CRTBindTexturesPass(IRTPass *pParent): IRTPass( pParent )
	{
	}

/*
=================================================
	
=================================================
*/
	CRTBindTexturesPass::~CRTBindTexturesPass()
	{
	}
	
/*
=================================================
	
=================================================
*/
	bool CRTBindTexturesPass::Create(const pptex_array_t &aTextures)
	{
		CHECK_ARGUMENT( !aTextures.Empty() );

		_aTextures	= aTextures;
		_pProc		= & _s_Process;
		return true;
	}
	
/*
=================================================
	
=================================================
*/
	bool CRTBindTexturesPass::ProcessMessage(TRTMessage &sMsg)
	{
		if ( --sMsg.iDepth <= 0 )
			return false;

		if ( sMsg.eType == TRTMessage::GET_PASS && sMsg.sData.sPass.eType == GetPassType() )
		{
			sMsg.sData.sPass.pPass = this;
			return true;
		}

		if ( sMsg.eType == TRTMessage::GET_LIGHT_AND_SHADOW_SETUP )
		{
			sMsg.sData.sPasses.pLast = this;
		}

		return _pParent->ProcessMessage( sMsg );
	}

/*
=================================================
	
=================================================
*/
	void CRTBindTexturesPass::_s_Process(void *pThis)
	{
		CRTBindTexturesPass	*	p_this = (CRTBindTexturesPass *)pThis;

		for (usize i = 0; i < p_this->_aTextures.Count(); ++i)
			p_this->_aTextures[i].pTexture->Bind( uint8( p_this->_aTextures[i].eType ) );
	}



//---------------------- CRTSetRenderTargetsPass ------------------//
/*
=================================================
	
=================================================
*/
	CRTSetRenderTargetsPass::CRTSetRenderTargetsPass(IRTPass *pParent):
		IRTPass( pParent ), _pFBO(nullptr)
	{
	}

/*
=================================================
	
=================================================
*/
	CRTSetRenderTargetsPass::~CRTSetRenderTargetsPass()
	{
	}
		
/*
=================================================
	
=================================================
*/
	bool CRTSetRenderTargetsPass::Create(const render_target_array_t &aRTs)
	{
		TRTMessage	s_msg;
		s_msg.GetFBO();

		CHECK_RETURN( _pParent->ProcessMessage( s_msg ) );

		_pFBO		= s_msg.sData.pFBO;
		_aTargets	= aRTs;

		if ( _aTargets.Empty() )	_pProc = & _s_ResetTargets;
		else						_pProc = & _s_SetTargets;

		return true;
	}
		
/*
=================================================
	
=================================================
*/
	bool CRTSetRenderTargetsPass::ProcessMessage(TRTMessage &sMsg)
	{
		if ( --sMsg.iDepth <= 0 )
			return false;

		if ( sMsg.eType == TRTMessage::GET_PASS && sMsg.sData.sPass.eType == GetPassType() )
		{
			sMsg.sData.sPass.pPass = this;
			return true;
		}

		if ( sMsg.eType == TRTMessage::GET_LIGHT_AND_SHADOW_SETUP )
		{
			sMsg.sData.sPasses.pLast = this;
		}

		return _pParent->ProcessMessage( sMsg );
	}

/*
=================================================
	
=================================================
*/
	void CRTSetRenderTargetsPass::_s_SetTargets(void *pThis)
	{
		CRTSetRenderTargetsPass *	p_this = (CRTSetRenderTargetsPass *)pThis;
		
		p_this->_pFBO->DetachAll();

		for (usize i = 0; i < p_this->_aTargets.Count(); ++i)
		{
			p_this->_pFBO->AttachTexture( p_this->_aTargets[i].pTexture, p_this->_aTargets[i].eAttach,
										  p_this->_aTargets[i].uLayer, p_this->_aTargets[i].uLOD );
		}

		p_this->_pFBO->SetSize( GLOBAL::RENDER()->GetViewport()->Size() );
		CHECK_RETURN_R( p_this->_pFBO->CheckStatus(), );

		p_this->_pFBO->Bind();
	}
	
/*
=================================================
	
=================================================
*/
	void CRTSetRenderTargetsPass::_s_ResetTargets(void *pThis)
	{
		CRTSetRenderTargetsPass *	p_this = (CRTSetRenderTargetsPass *)pThis;

		p_this->_pFBO->Unbind();
	}



//------------------------- CRTCyclePass --------------------------//
/*
=================================================
	
=================================================
*/
	CRTCyclePass::CRTCyclePass(IRTPass *pParent):
		IRTPass( pParent ), _pPass(nullptr)
	{
	}

/*
=================================================
	
=================================================
*/
	CRTCyclePass::~CRTCyclePass()
	{
		if ( _pPass != nullptr )	_pPass->Free();
	}
	
/*
=================================================
	
=================================================
*/
	bool CRTCyclePass::Create(const ivec3 &vIter, IRTPass *pPass)
	{
		CHECK_ARGUMENT( pPass != nullptr );

		CHECK_VALUE(  vIter.z != 0 && (
					( vIter.z > 0 && vIter.x <= vIter.y ) ||
					( vIter.z < 0 && vIter.x >= vIter.y ) ) );

		_vIteration = ivec4( vIter, vIter.x );
		_pPass		= pPass;
		_pProc		= vIter.z > 0 ? & _s_Process : & _s_ProcessRev;
		return true;
	}
		
/*
=================================================
	
=================================================
*/
	bool CRTCyclePass::ProcessMessage(TRTMessage &sMsg)
	{
		if ( --sMsg.iDepth <= 0 )
			return false;

		if ( sMsg.eType == TRTMessage::GET_PASS && sMsg.sData.sPass.eType == GetPassType() )
		{
			sMsg.sData.sPass.pPass = this;
			return true;
		}

		if ( sMsg.eType == TRTMessage::GET_ITERATION )
		{
			sMsg.sData.pIVec4 = &_vIteration;
			return true;
		}

		if ( sMsg.eType == TRTMessage::GET_LIGHT_AND_SHADOW_SETUP )
		{
			sMsg.sData.sPasses.pLast = this;
		}

		return _pParent->ProcessMessage( sMsg );
	}

/*
=================================================
	
=================================================
*/
	void CRTCyclePass::_s_Process(void *pThis)
	{
		CRTCyclePass *	p_this = (CRTCyclePass *)pThis;
		ivec4 &			v_iter = p_this->_vIteration;

		for (v_iter.w = v_iter.x; v_iter.w <= v_iter.y; v_iter.w += v_iter.z)
		{
			p_this->_pPass->Process();
		}
	}
	
/*
=================================================
	
=================================================
*/
	void CRTCyclePass::_s_ProcessRev(void *pThis)
	{
		CRTCyclePass *	p_this = (CRTCyclePass *)pThis;
		ivec4 &			v_iter = p_this->_vIteration;

		for (v_iter.w = v_iter.x; v_iter.w >= v_iter.y; v_iter.w += v_iter.z)
		{
			p_this->_pPass->Process();
		}
	}



//------------------------ CRTCustomPass --------------------------//
/*
=================================================
	
=================================================
*/
	CRTCustomPass::CRTCustomPass(IRTPass *pParent): IRTPass( pParent )
	{
	}

/*
=================================================
	
=================================================
*/
	CRTCustomPass::~CRTCustomPass()
	{
		for (usize i = 0; i < _aPasses.Count(); ++i)
			_aPasses[i]->Free();
	}

/*
=================================================
	
=================================================
*/
	bool CRTCustomPass::Create(const rt_pass_array_t &aPasses)
	{
		CHECK_ARGUMENT( !aPasses.Empty() );

		_aPasses	= aPasses;
		_pProc		= & _s_Process;
		return true;
	}
		
/*
=================================================
	
=================================================
*/
	bool CRTCustomPass::ProcessMessage(TRTMessage &sMsg)
	{
		if ( --sMsg.iDepth <= 0 )
			return false;

		if ( sMsg.eType == TRTMessage::GET_PASS && sMsg.sData.sPass.eType == GetPassType() )
		{
			sMsg.sData.sPass.pPass = this;
			return true;
		}

		if ( sMsg.eType == TRTMessage::GET_LIGHT_AND_SHADOW_SETUP )
		{
			pod_array<IRTPass *> &	a_passes = *sMsg.sData.sPasses.pPasses;

			for (usize i = 0; i < _aPasses.Count(); ++i) {
				for (usize j = 0; j < a_passes.Count(); ++j) {
					if ( _aPasses[i] == a_passes[j] ) {
						_aPasses.FastErase( i-- );
						break;
					}
				}
			}

			sMsg.sData.sPasses.pLast = this;
		}

		return _pParent->ProcessMessage( sMsg );
	}

/*
=================================================
	
=================================================
*/
	void CRTCustomPass::_s_Process(void *pThis)
	{
		CRTCustomPass *	p_this = (CRTCustomPass *)pThis;

		for (usize i = 0; i < p_this->_aPasses.Count(); ++i)
		{
			p_this->_aPasses[i]->Process();
		}
	}



//------------------------- CRTBranchPass -------------------------//
/*
=================================================
	
=================================================
*/
	CRTBranchPass::CRTBranchPass(IRTPass *pParent):
		IRTPass( pParent ), _pThenPass(nullptr), _pElsePass(nullptr), _pBoolVal(nullptr)
	{
	}
	
/*
=================================================
	
=================================================
*/
	CRTBranchPass::~CRTBranchPass()
	{
		if ( _pThenPass != nullptr )	_pThenPass->Free();
		if ( _pElsePass != nullptr )	_pElsePass->Free();
	}
	
/*
=================================================
	
=================================================
*/
	bool CRTBranchPass::Create(const string &sName, IRTPass *pThen, IRTPass *pElse)
	{
		CHECK_ARGUMENT( !sName.Empty() && pThen != nullptr && pElse != nullptr );

		TRTMessage	s_msg;
		s_msg.GetBool( sName );

		CHECK_RETURN( _pParent->ProcessMessage( s_msg ) );

		_pBoolVal	= s_msg.sData.sBool.pBool;
		_pThenPass	= pThen;
		_pElsePass	= pElse;

		return true;
	}
	
/*
=================================================
	
=================================================
*/
	bool CRTBranchPass::ProcessMessage(TRTMessage &sMsg)
	{
		if ( --sMsg.iDepth <= 0 )
			return false;

		if ( sMsg.eType == TRTMessage::GET_PASS && sMsg.sData.sPass.eType == GetPassType() )
		{
			sMsg.sData.sPass.pPass = this;
			return true;
		}

		if ( sMsg.eType == TRTMessage::GET_LIGHT_AND_SHADOW_SETUP )
		{
			sMsg.sData.sPasses.pLast = this;
		}

		return _pParent->ProcessMessage( sMsg );
	}
	
/*
=================================================
	
=================================================
*/
	void CRTBranchPass::_s_Process(void *pThis)
	{
		CRTBranchPass *	p_this = (CRTBranchPass *)pThis;

		if ( *p_this->_pBoolVal )
			p_this->_pThenPass->Process();
		else
			p_this->_pElsePass->Process();
	}



//----------------------- CRTPostProcessPass ----------------------//
/*
=================================================
	
=================================================
*/
	CRTPostProcessPass::CRTPostProcessPass(IRTPass *pParent):
		IRTPass( pParent ), _pFBO(nullptr)
	{
	}
		
/*
=================================================
	
=================================================
*/
	CRTPostProcessPass::~CRTPostProcessPass()
	{
	}
	
/*
=================================================
	
=================================================
*/
	bool CRTPostProcessPass::Create(const IPostProcessBasePtr &pPostProcess, const pptex_array_t &aInput,
									const render_target_array_t &aOutput)
	{
		CHECK_ARGUMENT( pPostProcess != nullptr );
		ASSERT( !aOutput.Empty() && !aInput.Empty() );

		CHECK_RETURN( pPostProcess->GetPostProcess( _pPostProcess ) );
		_pPostProcess->InputTextures( aInput );
		
		TRTMessage	s_msg;
		s_msg.GetFBO();

		CHECK_RETURN( _pParent->ProcessMessage( s_msg ) );
		_pFBO		= s_msg.sData.pFBO;

		_aTargets	= aOutput;
		_pProc		= & _s_Process;
		return true;
	}
		
/*
=================================================
	
=================================================
*/
	bool CRTPostProcessPass::ProcessMessage(TRTMessage &sMsg)
	{
		if ( --sMsg.iDepth <= 0 )
			return false;

		if ( sMsg.eType == TRTMessage::GET_PASS && sMsg.sData.sPass.eType == GetPassType() )
		{
			sMsg.sData.sPass.pPass = this;
			return true;
		}

		if ( sMsg.eType == TRTMessage::GET_LIGHT_AND_SHADOW_SETUP )
		{
			sMsg.sData.sPasses.pLast = this;
		}

		return _pParent->ProcessMessage( sMsg );
	}
	
/*
=================================================
	
=================================================
*/
	void CRTPostProcessPass::_s_Process(void *pThis)
	{
		CRTPostProcessPass *	p_this = (CRTPostProcessPass *)pThis;

		for (usize i = 0; i < p_this->_aTargets.Count(); ++i)
		{
			p_this->_pFBO->AttachTexture( p_this->_aTargets[i].pTexture, p_this->_aTargets[i].eAttach,
										  p_this->_aTargets[i].uLayer, p_this->_aTargets[i].uLOD );
		}

		p_this->_pFBO->SetSize( GLOBAL::RENDER()->GetViewport()->Size() );
		CHECK_RETURN_R( p_this->_pFBO->CheckStatus(), );

		p_this->_pFBO->Bind();

		p_this->_pPostProcess->Process( 0, true );

		p_this->_pFBO->Unbind();
	}



//------------------------ CRTSetGLStatesPass ---------------------//
/*
=================================================
	
=================================================
*/
	CRTSetGLStatesPass::CRTSetGLStatesPass(IRTPass *pParent): IRTPass( pParent )
	{
	}
	
/*
=================================================
	
=================================================
*/
	CRTSetGLStatesPass::~CRTSetGLStatesPass()
	{
	}
		
/*
=================================================
	
=================================================
*/
	bool CRTSetGLStatesPass::Create(const glstate_array_t &aStates, const TGLRenderState &sState,
									e_render_state_lock::type eRSLock)
	{
		_eRSLock	= eRSLock;
		_sState		= sState;
		_aStates	= aStates;
		_pProc		= & _s_Process;
		return true;
	}
	
/*
=================================================
	
=================================================
*/
	bool CRTSetGLStatesPass::ProcessMessage(TRTMessage &sMsg)
	{
		if ( --sMsg.iDepth <= 0 )
			return false;

		if ( sMsg.eType == TRTMessage::GET_PASS && sMsg.sData.sPass.eType == GetPassType() )
		{
			sMsg.sData.sPass.pPass = this;
			return true;
		}

		if ( sMsg.eType == TRTMessage::GET_LIGHT_AND_SHADOW_SETUP )
		{
			sMsg.sData.sPasses.pLast = this;
		}

		return _pParent->ProcessMessage( sMsg );
	}
	
/*
=================================================
	
=================================================
*/
	void CRTSetGLStatesPass::_s_Process(void *pThis)
	{
		CRTSetGLStatesPass *	p_this = (CRTSetGLStatesPass *)pThis;

		p_this->_sState.Apply( p_this->_eRSLock );
		
		for (usize i = 0; i < p_this->_aStates.Count(); ++i)
		{
			GLOBAL::SMAN()->SetState( p_this->_aStates[i].eState, p_this->_aStates[i].bEnabled );
		}
	}



//-------------------------- CRTSwizzlePass -----------------------//
/*
=================================================
	
=================================================
*/
	CRTSwizzlePass::CRTSwizzlePass(IRTPass *pParent):
		IRTPass(pParent), _pValue(nullptr), _pDefault(nullptr)
	{
	}

/*
=================================================
	
=================================================
*/
	CRTSwizzlePass::~CRTSwizzlePass()
	{
		for (usize i = 0; i < _aCase.Count(); ++i) {
			_aCase[i].pPass->Free();
		}
		_aCase.Clear();

		if ( _pDefault != nullptr )
			_pDefault->Free();
	}
	
/*
=================================================
	
=================================================
*/
	bool CRTSwizzlePass::Create(const string &sValue, const case_array_t &aCase, IRTPass *pDefault)
	{
		CHECK_ARGUMENT( !aCase.Empty() );

		TRTMessage	s_msg;
		s_msg.GetInt( sValue );
		CHECK_RETURN( _pParent->ProcessMessage( s_msg ) );

		_pValue		= (uint *)s_msg.sData.sInt.pInt;
		_aCase		= aCase;
		_pProc		= & _s_Process;
		_pDefault	= pDefault;

		return true;
	}
		
/*
=================================================
	
=================================================
*/
	bool CRTSwizzlePass::ProcessMessage(TRTMessage &sMsg)
	{
		if ( --sMsg.iDepth <= 0 )
			return false;

		if ( sMsg.eType == TRTMessage::GET_PASS && sMsg.sData.sPass.eType == GetPassType() )
		{
			sMsg.sData.sPass.pPass = this;
			return true;
		}

		if ( sMsg.eType == TRTMessage::GET_LIGHT_AND_SHADOW_SETUP )
		{
			sMsg.sData.sPasses.pLast = this;
		}

		return _pParent->ProcessMessage( sMsg );
	}

/*
=================================================
	
=================================================
*/
	void CRTSwizzlePass::_s_Process(void *pThis)
	{
		CRTSwizzlePass *	p_this  = (CRTSwizzlePass *)pThis;
		const uint			u_value = *p_this->_pValue;

		for (usize i = 0; i < p_this->_aCase.Count(); ++i)
		{
			if ( u_value == p_this->_aCase[i].uValue )
			{
				p_this->_aCase[i].pPass->Process();
				return;
			}
		}

		if ( p_this->_pDefault != nullptr )
			p_this->_pDefault->Process();
	}

//-------------------------------------------------------------------

}	// RenderUtils
}	// Engine